package com.kgcx.schedule.service;

import com.kgcx.common.client.organization.DriverClient;
import com.kgcx.common.client.organization.pojo.Driver;
import com.kgcx.common.constant.Cons;
import com.kgcx.common.constant.ErrorCode;
import com.kgcx.core.exception.BusinessException;
import com.kgcx.core.utils.HelpUtil;
import com.kgcx.dbutils.core.mapper.BaseMapper;
import com.kgcx.dbutils.core.model.ParameterMap;
import com.kgcx.schedule.domain.ChangeDriverLogDomain;
import com.kgcx.schedule.domain.DriverInfoDomain;
import com.kgcx.schedule.domain.ScheduleRecordDomain;
import com.kgcx.schedule.mapper.ChangeDriverLogMapper;
import com.kgcx.schedule.mapper.DriverInfoMapper;
import com.kgcx.schedule.mapper.ScheduleRecordHisMapper;
import com.kgcx.schedule.mapper.ScheduleRecordMapper;
import com.kgcx.schedule.rabbitmq.producer.OrderScheduledProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;

@Service
public class ScheduleRecordService extends BaseBusinessService<String, ScheduleRecordDomain> {
    private static final Logger LOG = LoggerFactory.getLogger(ScheduleRecordService.class);

    @Autowired
    private ScheduleRecordMapper scheduleRecordMapper;
    @Autowired
    private ScheduleRecordHisMapper scheduleRecordHisMapper;
    @Autowired
    private ChangeDriverLogMapper changeDriverLogMapper;
    @Autowired
    private DriverInfoMapper driverInfoMapper;

    @Autowired
    private DriverStateService driverStateService;
    @Autowired
    private ScheduleNotifyService scheduleNotifyService;
    @Autowired
    protected OrderScheduledProducer orderScheduledProducer;

    @Autowired
    private DriverClient driverClient;

    @Override
    protected BaseMapper<String, ScheduleRecordDomain> getMapper() {
        return scheduleRecordMapper;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public Integer selectRecordCount(ParameterMap params) {
        return scheduleRecordMapper.selectRecordCount(params);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 取消订单时，需要移除派单记录到历史表
     * 
     * @param record
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelRecord(String orderNo) {
        LOG.info(String.format("取消订单[%s]", orderNo));
        ScheduleRecordDomain record = scheduleRecordMapper.selectEntity(orderNo);
        if (record == null) {
            LOG.warn(String.format("未找到订单[%s]的派单记录！", orderNo));
            return;
        }

        record.setSchedule_status(ScheduleRecordDomain.STATUS_CANCELLED);

        archiveScheduleRecord(record);
    }

    /**
     * 行程结束时，更新结束时间，用于定时解锁司机
     * 
     * @param orderNo
     * @param finishTime
     */
    @Transactional(rollbackFor = Exception.class)
    public ScheduleRecordDomain finishRecord(String orderNo, Timestamp finishTime) {
        LOG.info(String.format("结束订单[%s]", orderNo));
        ScheduleRecordDomain record = scheduleRecordMapper.selectEntity(orderNo);
        if (record == null) {
            LOG.warn(String.format("未找到订单[%s]的派单记录！", orderNo));
            return null;
        }

        record.setActual_finish_time(finishTime == null ? HelpUtil.getNowTime() : finishTime);
        record.setSchedule_status(ScheduleRecordDomain.STATUS_FINISHED);
        scheduleRecordMapper.updateEntity(record);

        return record;
    }

    /**
     * 移入历史表
     * 
     * @param
     */
    @Transactional(rollbackFor = Exception.class)
    public void archiveScheduleRecord(String orderNo) {
        LOG.info(String.format("归档订单[%s]", orderNo));
        ScheduleRecordDomain record = scheduleRecordMapper.selectEntity(orderNo);
        if (record == null) {
            LOG.warn(String.format("未找到订单[%s]的派单记录！", orderNo));
            return;
        }

        archiveScheduleRecord(record);
    }

    @Transactional(rollbackFor = Exception.class)
    public void archiveScheduleRecord(ScheduleRecordDomain record) {
        LOG.info(String.format("归档订单[%s]", record.getOrder_no()));

        record.setArchive_time(HelpUtil.getNowTime());

        scheduleRecordHisMapper.insertEntity(record);
        scheduleRecordMapper.deleteEntity(record.getOrder_no());
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 派单记录改派司机（手动派单）
     * 
     * @param order_no
     * @param driver_id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ScheduleRecordDomain changeDriver(String order_no, Long driver_id, Long create_id) {
        ScheduleRecordDomain record = validateRecord(order_no);
        Driver driver = validateDriver(driver_id);
        ChangeDriverLogDomain changeDriverLog = buildChangeDriverLog(create_id, record, driver);

        try {
            record.setAuto_id(changeDriverLog.getNew_auto_id());
            record.setAuto_no(changeDriverLog.getNew_auto_no());
            record.setDriver_id(changeDriverLog.getNew_driver_id());
            record.setDriver_name(changeDriverLog.getNew_driver_name());
            record.setDriver_phone(changeDriverLog.getNew_driver_phone());
            record.setSchedule_status(ScheduleRecordDomain.STATUS_SCHEDULED);
            scheduleRecordMapper.updateEntity(record);

            LOG.info(String.format("订单[%s]，锁定司机[%d]成功。", record.getOrder_no(), record.getDriver_id()));

            changeDriverLogMapper.insertEntity(changeDriverLog);

            // 通知旧司机订单改派成功
            if (changeDriverLog.getOld_driver_id() != null) { // 非手动派单，而是改派时会有老死机ID
                // 这里需要发送MQ消息解锁旧司机
                ScheduleRecordDomain oldRecord = new ScheduleRecordDomain();
                oldRecord.setOrder_no(changeDriverLog.getOrder_no());
                oldRecord.setDriver_id(changeDriverLog.getOld_driver_id());
                orderScheduledProducer.changeDriver(oldRecord);

                if (DriverStateService.SWITCH) {
                    driverStateService.unlockDriver(changeDriverLog.getOld_driver_id());
                }
                scheduleNotifyService.notifyOldDriver(record, changeDriverLog.getOld_driver_id());
            }

            if (DriverStateService.SWITCH) {
                driverStateService.orderScheduled(record.getDriver_id(), record.getOrder_no());
            }

            // 发送派单成功的MQ消息
            orderScheduledProducer.scheduleSuccessful(record);

            // 通知新司机订单来了
            scheduleNotifyService.notifyDriver(record, driver);

            return record;
        } catch (DuplicateKeyException e) { // 如果抛出了唯一键冲突异常，则司机已经被其他订单锁定了
            LOG.info(String.format("订单[%s]，锁定司机[%d]失败，已被其他订单锁定。", record.getOrder_no(), driver.getKid()));
            throw new BusinessException(ErrorCode.SCHEDULE_DRIVER_SCHEDULING,
                    String.format("司机[%s]已接了其他订单！", driver.getName()));
        } catch (Exception e) { // 如果抛出了其他异常
            LOG.error(String.format("订单[%s]，锁定司机[%d]失败，其他异常！", record.getOrder_no(), driver.getKid()), e);
            throw new BusinessException(ErrorCode.SCHEDULE_UNKNOWN_EXCEPTION,
                    String.format("其他未知异常，请联系系统技术支持人员！", order_no));
        }
    }

    private ScheduleRecordDomain validateRecord(String order_no) {
        ScheduleRecordDomain record = scheduleRecordMapper.selectEntity(order_no);
        if (record == null) {
            throw new BusinessException(ErrorCode.SCHEDULE_ORDER_NOT_EXIST, String.format("订单[%s]不存在！", order_no));
        }
        if (ScheduleRecordDomain.STATUS_FINISHED.equals(record.getSchedule_status())) {
            throw new BusinessException(ErrorCode.SCHEDULE_ORDER_FINISHED, String.format("订单[%s]已结束行程！", order_no));
        }
        if (ScheduleRecordDomain.STATUS_CANCELLED.equals(record.getSchedule_status())) {
            throw new BusinessException(ErrorCode.SCHEDULE_ORDER_CANCELLED, String.format("订单[%s]已被取消！", order_no));
        }

        return record;
    }

    private Driver validateDriver(Long driver_id) {
        if (DriverStateService.SWITCH) {
            return validateDriverInfo(driver_id);
        }

        Driver driver = driverClient.getDriver(driver_id, null);
        if (driver == null) {
            throw new BusinessException(ErrorCode.SCHEDULE_DRIVER_NOT_EXIST, String.format("司机[%d]不存在！", driver_id));
        }
        if (Cons.DRIVER_STATE_OFF.equals(driver.getState())) {
            throw new BusinessException(ErrorCode.SCHEDULE_DRIVER_OFF,
                    String.format("司机[%s]未上线，不能接单！", driver.getName()));
        }
        if (Cons.DRIVER_STATE_RUN.equals(driver.getState())) {
            throw new BusinessException(ErrorCode.SCHEDULE_DRIVER_RUN,
                    String.format("司机[%s]已有服务中订单，不能再接单！", driver.getName()));
        }

        return driver;
    }

    private Driver validateDriverInfo(Long driver_id) {
        DriverInfoDomain driverInfo = driverInfoMapper.selectEntity(driver_id);
        if (driverInfo == null) {
            throw new BusinessException(ErrorCode.SCHEDULE_DRIVER_NOT_EXIST, String.format("司机[%d]不存在！", driver_id));
        }
        if (Cons.DRIVER_STATE_OFF.equals(driverInfo.getDriver_state())) {
            throw new BusinessException(ErrorCode.SCHEDULE_DRIVER_OFF,
                    String.format("司机[%s]未上线，不能接单！", driverInfo.getDriver_name()));
        }
        if (Cons.DRIVER_STATE_RUN.equals(driverInfo.getDriver_state())) {
            throw new BusinessException(ErrorCode.SCHEDULE_DRIVER_RUN,
                    String.format("司机[%s]已有服务中订单，不能再接单！", driverInfo.getDriver_name()));
        }

        Driver driver = new Driver();
        driver.setKid(driverInfo.getDriver_id());
        driver.setCityId(driverInfo.getCity_id());
        driver.setAutoId(driverInfo.getAuto_id());
        driver.setAutoNo(driverInfo.getAuto_no());
        driver.setVehiclePurpose(driverInfo.getAuto_type());
        driver.setName(driverInfo.getDriver_name());
        driver.setPhone(driverInfo.getDriver_phone());
        driver.setLicenseId(driverInfo.getDriver_license_id());
        driver.setState(driverInfo.getDriver_state());
        driver.setLastUpdateStateTime(driverInfo.getState_update_time());
        driver.setAirportId(driverInfo.getAirport_id());
        driver.setStationId(driverInfo.getStation_id());

        return driver;
    }

    private ChangeDriverLogDomain buildChangeDriverLog(Long create_id, ScheduleRecordDomain record, Driver driver) {
        ChangeDriverLogDomain changeDriverLog = new ChangeDriverLogDomain();
        changeDriverLog.setOrder_no(record.getOrder_no());
        changeDriverLog.setCity_id(record.getCity_id());
        changeDriverLog.setOld_auto_id(record.getAuto_id());
        changeDriverLog.setOld_auto_no(record.getAuto_no());
        changeDriverLog.setOld_driver_id(record.getDriver_id());
        changeDriverLog.setOld_driver_name(record.getDriver_name());
        changeDriverLog.setOld_driver_phone(record.getDriver_phone());
        changeDriverLog.setNew_auto_id(driver.getAutoId());
        changeDriverLog.setNew_auto_no(driver.getAutoNo());
        changeDriverLog.setNew_driver_id(driver.getKid());
        changeDriverLog.setNew_driver_name(driver.getName());
        changeDriverLog.setNew_driver_phone(driver.getPhone());
        changeDriverLog.setCreate_id(create_id);
        changeDriverLog.setCreate_time(HelpUtil.getNowTime());
        return changeDriverLog;
    }
}
